home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / MIDIMOD2.ZIP / TEXTWINC.C < prev   
Text File  |  1993-06-26  |  14KB  |  587 lines

  1. /*
  2.  * TEXTWINC.C - Text-windowing system (Curses Version)
  3.  * (c)opyright Andrew Scott 1993
  4.  *
  5.  * Turbo C 2.0
  6.  *
  7.  * Description: Provides a simple text-menuing interface. Includes pop-down
  8.  *              menus, and scrolling windows. To use, set up a main window
  9.  *              with the MainWindow() function.
  10.  *   eg. MainWindow("Title", 2, "Files", 'f', "Help", 'h');
  11.  *
  12.  *              Then set the commands and command-numbers for each menu use
  13.  *              the SetMenu function.
  14.  *   eg. SetMenu(0, 3, "Open",'o',0, "Close",'c',1, "Quit",'q',2);
  15.  *       SetMenu(1, 3, "Help",'h',3, "------------",-1,-1, "About",'a',4);
  16.  *
  17.  *              Now just make a call to Choice() and the number it returns
  18.  *              will be the command-number of the choice
  19.  *   eg. If Help|About is chosen, 4 will be returned.
  20.  *
  21.  *              If you want to get the user to select an option from a list
  22.  *              of options, make an array of strings with the option
  23.  *              descriptions in them (ensure final element is NULL). The
  24.  *              element number chosen will be returned (first element = 0)
  25.  *              after a call to ScrollChoice()
  26.  *   eg. If the array is a, and the maximum string length is l, make the call
  27.  *       ScrollChoice("BetterTitle", a, l);
  28.  *
  29.  *              To display test inside a box, call DrawBox() with a NULL-
  30.  *              terminated array of strings.
  31.  *   eg. If the array is a, call
  32.  *       DrawBox(a);
  33.  *
  34.  *              To display text inside a box, and then wait for a key to be
  35.  *              pressed, set up a NULL-terminated array of strings and call
  36.  *              the InfoBox() function
  37.  *   eg. If the array is a, make the call
  38.  *       InfoBox(a);
  39.  *
  40.  *              If you want to get input as well as print a box, make a call
  41.  *              to DialogBox(), if nothing is entered, a default string is
  42.  *              returned
  43.  *   eg. DialogBox(a, "C:\") would return "C:\" if nothing was entered.
  44.  *
  45.  *
  46.  * Author: Andrew Scott (Adrenalin Software)
  47.  *
  48.  * Date: 14/3/1993 ver 0.1
  49.  *       17/4/1993   - converted to Curses (0.1a)
  50.  */
  51.  
  52. #include <curses.h>
  53. #include <stdlib.h>
  54. #include <stdio.h>
  55. #include <stdarg.h>
  56. #include <string.h>
  57. #include <ctype.h>
  58.  
  59. /* Maximum number of chars in a menu title */
  60. #define MAXMENUWIDTH 9
  61. /* Maximum number of chars in a menu command */
  62. #define MAXCOMMWIDTH 12
  63.  
  64. /* remove any old key definitions */
  65. #ifdef KEY_UP
  66. #undef KEY_UP
  67. #undef KEY_DOWN
  68. #undef KEY_LEFT
  69. #undef KEY_RIGHT
  70. #endif
  71.  
  72. /* wgetch returns these values for these keys when they're pressed */
  73. #define CR        '\r' /* sometimes '\n' */
  74. #define BS        '\b'
  75. #define DEL       127
  76. #define ESC       27
  77. #define KEY_DOWN  0402
  78. #define KEY_UP    0403
  79. #define KEY_LEFT  0404
  80. #define KEY_RIGHT 0405
  81.  
  82. typedef char *string;
  83.  
  84. struct MenuStruct {
  85.     char n[MAXCOMMWIDTH+1]; /* Item name ("" = no items) */
  86.     int k;                  /* Key-press selector (lower case) */
  87.     int x;                  /* Function reference number (-ve = no command) */
  88. };
  89. typedef struct MenuStruct *MenuItem;
  90.  
  91. struct MenuBar {
  92.     char n[MAXMENUWIDTH+1]; /* Menu name ("" = no menus) */
  93.     int k;                  /* Key-press selector (lower case) */
  94.     MenuItem m;             /* Menu items */
  95. } *MainW;
  96.  
  97. void MainWindow(string title, int num, ...)
  98. /* Post: Screen is set up with a nice title and num menus */
  99. {
  100.     va_list args;
  101.     string t;
  102.     int i;
  103.     struct MenuBar *c;
  104.  
  105.     c = MainW = (struct MenuBar *) malloc(sizeof(struct MenuBar) * (num + 1));
  106.     va_start(args, num);
  107.     initscr();
  108.     raw();        /* startup mode.. use either this or cbreak() */
  109.     noecho();
  110.     keypad(stdscr, TRUE);
  111.     scrollok(stdscr, FALSE);
  112.     werase(stdscr);
  113.     wmove(stdscr, 0, (COLS - strlen(title))/2);
  114.     wprintw(stdscr, "%s", title);
  115.     wmove(stdscr, 1, 1);
  116.     for (i = num; i--; c++) {
  117.         t = (va_arg(args, string));
  118.         strcpy(c->n, t);
  119.         wprintw(stdscr, "%-*s", MAXMENUWIDTH, t);
  120.         c->k = (va_arg(args, int));
  121.         c->m = NULL;
  122.     }
  123.     c->n[0] = 0;
  124.     va_end(args);
  125.     refresh();
  126. }
  127.  
  128. void EndWindows()
  129. /* Post: Windows are freed up */
  130. {
  131.     struct MenuBar *c;
  132.     MenuItem t;
  133.  
  134.     for (c = MainW; c->n[0]; c++)
  135.         free(c->m);
  136.     free(MainW);
  137.     delwin(curscr);
  138.     endwin();
  139. }
  140.  
  141. void SetMenu(int menu, int n, ...)
  142. /* Post: n commands has been set in menu #menu (0 = 1st) */
  143. {
  144.     va_list args;
  145.     string t;
  146.     struct MenuBar *c1;
  147.     MenuItem c2;
  148.  
  149.     int i;
  150.  
  151.     for (i = menu, c1 = MainW; i-- && c1->n[0]; c1++);
  152.     if (! c1->n[0])
  153.         return; /* error */
  154.     c1->m = c2 = (MenuItem) malloc(sizeof(struct MenuStruct) * (n + 1));
  155.     va_start(args, n);
  156.     for (; n--; c2++) {
  157.         t = va_arg(args, string);
  158.         strcpy(c2->n, t);
  159.         c2->k = va_arg(args, int);
  160.         c2->x = va_arg(args, int);
  161.     }
  162.     c2->n[0] = 0;
  163. }
  164.  
  165. WINDOW *MenuWin = NULL; /* ignore this unstructured global variable ;) */
  166.  
  167. void PrintMenu(MenuItem m, int x)
  168. /* Post: The menu which is pointed to by m is printed at column x */
  169. {
  170.     int i;
  171.     MenuItem t;
  172.  
  173.     if (MenuWin != NULL) {
  174.         werase(MenuWin);
  175.         wrefresh(MenuWin);
  176.         delwin(MenuWin);
  177.     }
  178.     for (i=0, t=m; t->n[0]; i++, t++);
  179.     MenuWin = newwin(i+2, MAXCOMMWIDTH+4, 2, x-1);
  180.     box(MenuWin, 0, 0);
  181.     for (i=1; m->n[0]; i++) {
  182.         wmove(MenuWin, i, 2);
  183.         waddstr(MenuWin, (m++)->n);
  184.     }
  185.     wrefresh(MenuWin);
  186. }
  187.  
  188. void ClearMenu()
  189. /* Post: The last menu printed is now gone */
  190. {
  191.     werase(MenuWin);
  192.     wrefresh(MenuWin);
  193. }
  194.  
  195. void PrintComm(MenuItem m, int y)
  196. /* Post: Item *m is highlighted in current menu, position y */
  197. {
  198.     wmove(MenuWin, y+1, 1);
  199.     wprintw(MenuWin, "[%-*s]", MAXCOMMWIDTH, m->n);
  200. }
  201.  
  202. void ClearComm(MenuItem m, int y)
  203. /* Post: Item *m is normalized in current menu, position y */
  204. {
  205.     wmove(MenuWin, y+1, 1);
  206.     wprintw(MenuWin, " %-*s ", MAXCOMMWIDTH, m->n);
  207. }
  208.  
  209. void PrintBar(WINDOW *SWin, string s, int w, int y)
  210. /* Post: String s (width w) is highlighted in the window SWin on line y */
  211. {
  212.     wmove(SWin, y, 0);
  213.     wprintw(SWin, "[%-*s]", w, s);
  214. }
  215.  
  216. void ClearBar(WINDOW *SWin, string s, int w, int y)
  217. /* Post: String s (width w) is normalized in the window SWin on line y */
  218. {
  219.     wmove(SWin, y, 0);
  220.     wprintw(SWin, " %-*s ", w, s);
  221. }
  222.  
  223. void Beep()
  224. /* Post: Speaker has made a beep */
  225. {
  226.     beep(); /* delete this line if your curses throw up on it */
  227. }
  228.  
  229. int Choice()
  230. /*
  231.  * Returns: a function reference number corresponding to a chosen command,
  232.  *   but returns -1 on an error.
  233.  */
  234. {
  235.     int ch;
  236.     struct MenuBar *c;
  237.     MenuItem p, q;
  238.     int i, fx = -1, y;
  239.  
  240.     do {
  241.         wmove(stdscr, LINES-1, 0);
  242.         wprintw(stdscr, "Press the letter of the menu you wish to select.");
  243.         refresh();
  244.         ch = tolower(wgetch(stdscr));
  245.         for (c = MainW, i = 0; c->n[0] && c->k != ch; c++, i++);
  246.         i = MAXMENUWIDTH * i;
  247.         if (c->n[0] != 0)
  248.             do {
  249.                 wmove(stdscr, LINES-1, 0);
  250.                 wprintw(stdscr, "Press a letter, or use cursor keys and <RETURN> to select a command.");
  251.                 wmove(stdscr, 1, i);
  252.                 wprintw(stdscr, "[%s]", c->n);
  253.                 refresh();
  254.                 PrintMenu(p = c->m, i+1);
  255.                 PrintComm(p, y = 0);
  256.                 do {
  257.                     wrefresh(MenuWin);
  258.                     ch = tolower(wgetch(MenuWin));
  259.                     if (ch==KEY_UP) /** up-arrow */
  260.                         if (!y)
  261.                             Beep();
  262.                         else {
  263.                             ClearComm(p--, y--);
  264.                             PrintComm(p, y);
  265.                         }
  266.                     else if (ch==KEY_DOWN) /** down-arrow */
  267.                         if (! (p+1)->n[0])
  268.                             Beep();
  269.                         else {
  270.                             ClearComm(p++, y++);
  271.                             PrintComm(p, y);
  272.                         }
  273.                     else {
  274.                         for (q = c->m; q->n[0] && q->k != ch; q++);
  275.                         if (q->n[0])
  276.                             fx = q->x;
  277.                     }
  278.                 } while ((ch==KEY_UP || ch==KEY_DOWN) && fx < 0);
  279.                 if (ch==CR && fx < 0) /** return */
  280.                     fx = p->x;
  281.                 ClearMenu();
  282.                 wmove(stdscr, 1, i);
  283.                 wprintw(stdscr, " %s ", c->n);
  284.                 wmove(stdscr, LINES-1, 0);
  285.                 wclrtoeol(stdscr);
  286.                 if (ch==KEY_LEFT) /** left-arrow */
  287.                     if (!i)
  288.                         Beep();
  289.                     else {
  290.                         i -= MAXMENUWIDTH;
  291.                         c--;
  292.                     }
  293.                 else if (ch==KEY_RIGHT) /** right-arrow */
  294.                     if (! (c+1)->n[0])
  295.                         Beep();
  296.                     else {
  297.                         i += MAXMENUWIDTH;
  298.                         c++;
  299.                     }
  300.             } while (ch != ESC && fx < 0);
  301.     } while (fx < 0);
  302.     refresh();
  303.     return fx;
  304. }
  305.  
  306. void ClearWin()
  307. /* Post: Area below command and title bars is clear */
  308. {
  309.     wmove(stdscr, 2, 0);
  310.     wclrtobot(stdscr);
  311.     refresh();
  312. }
  313.  
  314. int ScrollChoice(string title, string *sp, int w)
  315. /*
  316.  * Returns: The offset of the string from s which is chosen, -1 on none
  317.  *    s points to the start of a list of strings, max length w, NULL term.
  318.  */
  319. {
  320.     string *srt, *end;
  321.     int l, p, cur, ch, x = -1;
  322.     WINDOW *ScrWin, *BorderWin;
  323.  
  324.     if (*sp==NULL) /* have to have at least 1 thing to print */
  325.         return -1;
  326.     if (strlen(title) > w)
  327.         w = strlen(title);
  328.     wmove(stdscr, LINES-1, 0);
  329.     wprintw(stdscr, "Use cursor keys and <RETURN> to make a selection.");
  330.     refresh();
  331.     for (end = sp, l = 0; *end!=NULL && l < LINES-5; end++, l++);
  332.     BorderWin = newwin(l+2, w+4, 2, 4);
  333.     box(BorderWin, 0, 0);
  334.     wrefresh(BorderWin);
  335.     ScrWin = newwin(l, w+2, 3, 5);
  336.     scrollok(ScrWin, FALSE);
  337.     keypad(ScrWin, TRUE);
  338.     p = l;
  339.     srt = end;
  340.     while (p--) {
  341.         wmove(ScrWin, p, 1);
  342.         waddstr(ScrWin, *(--srt));
  343.     }
  344.     PrintBar(ScrWin, *srt, w, p = 0);
  345.     cur = 0;
  346.     do {
  347.         wrefresh(ScrWin);
  348.         ch = tolower(wgetch(ScrWin));
  349.         if (ch==KEY_UP)
  350.             if (!cur)
  351.                 Beep();
  352.             else if (p) {
  353.                 ClearBar(ScrWin, *(srt+p), w, p);
  354.                 p--;
  355.                 PrintBar(ScrWin, *(srt+p), w, p);
  356.                 cur--;
  357.             } else {
  358.                 ClearBar(ScrWin, *(srt--), w, 0);
  359.                 wmove(ScrWin, 0, 0);
  360.                 winsertln(ScrWin);
  361.                 PrintBar(ScrWin, *srt, w, 0);
  362.                 end--;
  363.                 cur--;
  364.             }
  365.         else if (ch==KEY_DOWN)
  366.             if (p==l-1 && *end==NULL)
  367.                 Beep();
  368.             else if (p < l-1) {
  369.                 ClearBar(ScrWin, *(srt+p), w, p);
  370.                 p++;
  371.                 cur++;
  372.                 PrintBar(ScrWin, *(srt+p), w, p);
  373.             } else {
  374.                 ClearBar(ScrWin, *(end-1), w, p);
  375.                 wmove(ScrWin, 0, 0);
  376.                 wdeleteln(ScrWin);
  377.                 cur++;
  378.                 PrintBar(ScrWin, *end, w, p);
  379.                 end++;
  380.                 srt++;
  381.             }
  382.         else if (ch==CR)
  383.             x = cur;
  384.     } while (x < 0 && ch != ESC);
  385.     delwin(ScrWin);
  386.     werase(BorderWin);
  387.     wrefresh(BorderWin);
  388.     delwin(BorderWin);
  389.     ClearWin();
  390.     return x;
  391. }
  392.  
  393. WINDOW *PrintBox(string *sp, int *x, int *y, int *w)
  394. /*
  395.  * Returns: The window in which the NULL-terminated array of strings pointed
  396.  *    to by sp is printed, y is set to be the offset to the bottom of the
  397.  *    window, and w is the width of it.
  398.  */
  399. {
  400.     int w1, i, j, k;
  401.     string *c;
  402.     WINDOW *BoxWin;
  403.  
  404.     if (*sp==NULL)
  405.         return NULL;
  406.     c = sp;
  407.     for (w1 = strlen(*(c++)), i = 1; *c!=NULL; c++, i++)
  408.         if (w1 < (j = strlen(*c)))
  409.             w1 = j;
  410.     j = 1 + (LINES - 5 - i)/2;
  411.     *y = j + i;
  412.     *w = k = w1 += 2;
  413.     w1 = (COLS - 2 - w1)/2;
  414.     *x = w1 + 1;
  415.     BoxWin = newwin(i+2, k+2, j, w1);
  416.     box(BoxWin, 0, 0);
  417.     for (i = 1, c = sp; *c!=NULL; c++) {
  418.         wmove(BoxWin, i++, 2);
  419.         waddstr(BoxWin, *c);
  420.     }
  421.     wrefresh(BoxWin);
  422.     return BoxWin;
  423. }
  424.  
  425. void DrawBox(string *sp)
  426. /* Post: The NULL-terminated array of strings pointed to by sp is printed */
  427. {
  428.     int k;
  429.  
  430.     delwin(PrintBox(sp, &k, &k, &k));
  431. }
  432.  
  433. char InfoBox(string *sp)
  434. /*
  435.  * Returns: The key pressed after the NULL-terminated array of strings
  436.  *    pointed to by sp is printed.
  437.  */
  438. {
  439.     int k;
  440.     WINDOW *BoxWin;
  441.  
  442.     wmove(stdscr, LINES-1, 0);
  443.     waddstr(stdscr, "Please press a key.");
  444.     refresh();
  445.     BoxWin = PrintBox(sp, &k, &k, &k);
  446.     k = wgetch(stdscr);
  447.     werase(BoxWin);
  448.     wrefresh(BoxWin);
  449.     delwin(BoxWin);
  450.     ClearWin();
  451.     return k;
  452. }
  453.  
  454. string DialogBox(string *sp, string def)
  455. /* Pre: def != NULL */
  456. /*
  457.  * Returns: The string entered (or def if none entered), after printing
  458.  *    box filled with NULL-terminated strings sp.
  459.  */
  460. {
  461.     int x, y, w, ch, i=0;
  462.     string s;
  463.     WINDOW *DiagWin, *TextWin;
  464.  
  465.     wmove(stdscr, LINES-1, 0);
  466.     waddstr(stdscr, "Enter text, and when you are finished, press <RETURN>.");
  467.     refresh();
  468.     DiagWin = PrintBox(sp, &x, &y, &w);
  469.     s = (string) malloc(w+1);
  470.     strcpy(s, def);
  471.     TextWin = newwin(1, w, y, x);
  472.     scrollok(TextWin, FALSE);
  473.     keypad(TextWin, TRUE);
  474.     wmove(TextWin, 0, 0);
  475.     waddstr(TextWin, s);
  476.     wmove(TextWin, 0, 0);
  477.     x = strlen(s);
  478.     do {
  479.         wrefresh(TextWin);
  480.         ch = wgetch(TextWin);
  481.         if (ch==BS || ch==DEL)
  482.             if (!i)
  483.                 Beep();
  484.             else {
  485.                 wprintw(TextWin, "\b");
  486.                 wclrtoeol(TextWin);
  487.                 x = --i;
  488.             }
  489.         else if (ch==KEY_LEFT)
  490.             if (!i)
  491.                 Beep();
  492.             else {
  493.                 wprintw(TextWin, "\b");
  494.                 i--;
  495.             }
  496.         else if (ch==KEY_RIGHT)
  497.             if (i==x)
  498.                 Beep();
  499.             else {
  500.                 waddch(TextWin, s[i]);
  501.                 i++;
  502.             }
  503.         else if (ch>31 && ch<127)
  504.             if (i==w-1)
  505.                 Beep();
  506.             else {
  507.                 waddch(TextWin, ch);
  508.                 if (i==x)
  509.                     x++;
  510.                 s[i++] = ch;
  511.             }
  512.     } while (ch!=CR && ch!=ESC);
  513.     s[i] = 0;
  514.     if (ch==ESC) {
  515.         free(s);
  516.         strcpy(s = (string) malloc(strlen(def)+1), def);
  517.     }
  518.     delwin(TextWin);
  519.     werase(DiagWin);
  520.     wrefresh(DiagWin);
  521.     delwin(DiagWin);
  522.     ClearWin();
  523.     return s;
  524. }
  525.  
  526. /*
  527.  * This main function is for testing the basics of these routines.
  528.  * Leave it commented out when creating object files.
  529.  *
  530. main()
  531. {
  532.     string scrolly[] = {
  533.         "First one",
  534.         "Second option",
  535.         "3rd",
  536.         "4th",
  537.         "Fifth",
  538.         "This could go on for AGES",
  539.         "Why not stop soon?",
  540.         "Ok",
  541.         "How about the next one",
  542.         "Done",
  543.         "Nope",
  544.         "Here's another few..",
  545.         "Un",
  546.         "Deux",
  547.         "Trois",
  548.         "Quatre",
  549.         "When should I stop?",
  550.         "Not yet obviously.",
  551.         "Cinq",
  552.         "Six",
  553.         "Sept",
  554.         "I think that's enough",
  555.         "Well maybe one more.",
  556.         NULL
  557.     };
  558.     string diagbox[] = {
  559.         "Please enter some gibberish",
  560.         "below at the prompt please.",
  561.         "It WONT check your spelling.",
  562.         "",
  563.         "",
  564.         NULL
  565.     };
  566.     string s;
  567.  
  568.     MainWindow("Big Huge Title", 3, "Ace", 'a', "Bee", 'b', "seaCide", 'c');
  569.     SetMenu(0, 3, "First", 'f', 1, "------------", -1, -1, "Second", 's', 2);
  570.     SetMenu(1, 1, "Spelling", 's', 3);
  571.     SetMenu(2, 2, "I'd like", 'i', 4, "To be beside", 't', 5);
  572.     while (1)
  573.         switch (Choice()) {
  574.             case 1:
  575.                 ScrollChoice("Window Name", scrolly, 25);
  576.                 break;
  577.             case 3:
  578.                 s = DialogBox(diagbox, "sample");
  579.                 free(s);
  580.                 break;
  581.             case 5:
  582.                 exit(0);
  583.         }
  584. }
  585.  * Ok.. this ends the commented out part
  586.  */
  587.